﻿using AutoMapper;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.EntityFrameworkCore;
using StarsAdmin.Application.Api.Dtos;
using StarsAdmin.Application.Dict.Dtos;
using StarsAdmin.Application.Menu.Dtos;
using StarsAdmin.Core.Attributes;
using StarsAdmin.Core.Cache;
using StarsAdmin.Core.Consts;
using StarsAdmin.Core.DB.Entities;
using StarsAdmin.Core.Exceptions;
using StarsAdmin.Core.Extensions;
using StarsAdmin.Core.Models;
using StarsAdmin.Repository.Api;

namespace StarsAdmin.Application.Api.Services
{
    public class ApiService : IApiService
    {
        private readonly IMapper _mapper;
        private readonly ICacheService _cache;
        private readonly IApiRepository _apiRepository;
        private readonly IApiDescriptionGroupCollectionProvider _apiDesGroup;

        public ApiService(IMapper mapper, ICacheService cacheService, IApiDescriptionGroupCollectionProvider apiDes, IApiRepository apiRepository)
        {
            _mapper = mapper;
            _cache = cacheService;
            _apiDesGroup = apiDes;
            _apiRepository = apiRepository;
        }

        #region 查询

        public async Task<List<ApiDto>> GetAllAsync(BaseQueryInput input)
        {
            var result = await _apiRepository.GetAll().AsNoTracking()
                                     .WhereIf(input != null && input.QueryConditions.Any(), input.QueryConditions)
                                     .OrderByIf(input != null && input.Sort != null, input.Sort, input.Order != false)
                                     .ToTreeAsync();
            return _mapper.Map<List<ApiDto>>(result);
        }

        public async Task<Page<ApiDto>> GetPageAsync(PageQueryInput input)
        {
            var result = await _apiRepository.GetAll().AsNoTracking()
                    .WhereIf(input != null && input.QueryConditions.Any(), input.QueryConditions)
                    .OrderByIf(input != null && input.Sort != null, input.Sort, input.Order != false)
                    .ToPageListAsync(input.CurrentPage, input.PageSize);
            return _mapper.Map<Page<ApiDto>>(result);
        }

        public async Task<ApiDto?> GetByIdAsync(long id)
        {
            var result = await _apiRepository.GetByIdAsync(id);
            return _mapper.Map<ApiDto>(result);
        }

        public async Task<List<ApiDto>> GetByIdsAsync(long[] ids)
        {
            var result = await _apiRepository.GetByIds(ids).ToListAsync();
            return _mapper.Map<List<ApiDto>>(result);
        }

        #endregion 查询

        #region 同步

        public async Task<bool> UpdateAsync(UpdateApiInput input)
        {
            var api = await _apiRepository.GetByIdAsync(input.Id);
            if (api == null)
            {
                throw new NotFoundException("未找到指定的接口。");
            }
            var entity = _mapper.Map(input, api);
            var result = await _apiRepository.UpdateAndSaveAsync(entity);
            if (result)
            {
                await _cache.RemoveAsync(CacheKeyConst.ApiCacheKey);
            }
            return result;
        }

        public async Task<bool> EnabledAsync(long id)
        {
            var entity = await _apiRepository.GetByIdAsync(id);
            if (entity == null)
            {
                throw new NotFoundException("未找到指定的接口。");
            }
            entity.IsEnabled = !entity.IsEnabled;
            var result = await _apiRepository.UpdateAndSaveAsync(entity);
            if (result)
            {
                await _cache.RemoveAsync(CacheKeyConst.ApiCacheKey);
            }
            return result;
        }

        public async Task<bool> SyncApiAsync()
        {
            var result = true;
            List<ApiEntity> updateApis = new List<ApiEntity>();
            List<ApiEntity> newApis = new List<ApiEntity>();

            // 按照控制器名称分组
            var controllerGroup = _apiDesGroup.ApiDescriptionGroups.Items.SelectMany(x => x.Items).GroupBy(x => ((ControllerActionDescriptor)x.ActionDescriptor).ControllerName).ToList();
            // 获取所有数据（包含假删除的数据）
            var apis = _apiRepository.Get().AsNoTracking();
            // 遍历所有控制器
            foreach (var controller in controllerGroup)
            {
                if (!controller.Any()) continue;
                // 获得控制器注释
                var summary = await controller.GetXmlDocsCommentsAsync();
                ApiEntity controlleraApiEntity = new ApiEntity()
                {
                    Name = controller.Key,
                    Label = summary,
                    Path = controller.Key,
                };
                // 获得所有ActionDescriptor的属性标记，并筛选出OrderAttribute属性标记
                var objOrderAttribute = controller.FirstOrDefault()?.ActionDescriptor.EndpointMetadata.FirstOrDefault(x => x is OrderAttribute);
                // 获得OrderAttribute的值
                if (objOrderAttribute != null)
                {
                    var orderAttribute = objOrderAttribute as OrderAttribute;
                    controlleraApiEntity.Sort = orderAttribute?.Value ?? 100;
                }
                // 从数据库查看当前控制器数据是否存在
                var controllerApi = await apis.FirstOrDefaultAsync(e => e.Name == controlleraApiEntity.Name);

                int sort = 1;
                foreach (var apiDescription in controller)
                {
                    // 获取当前ApiDescription的所有的ActionDescriptor转换为ControllerActionDescriptor
                    var actionDescriptor = apiDescription.ActionDescriptor as ControllerActionDescriptor;
                    // 获取方法的名称
                    var methodType = actionDescriptor?.DisplayName?.Split('(').First();

                    ApiEntity actionEntity = new ApiEntity();
                    actionEntity.Label = await methodType?.GetXmlDocsCommentsAsync();
                    actionEntity.HttpMethod = apiDescription.HttpMethod;
                    actionEntity.Path = "/" + apiDescription.RelativePath;
                    actionEntity.Name = actionDescriptor.ActionName;
                    actionEntity.Sort = sort;

                    // 从数据库查看当前方法数据是否存在
                    var actionApi = await apis.FirstOrDefaultAsync(e => e.Name == actionEntity.Name && e.Path == actionEntity.Path);
                    // 如果actionApi存在
                    if (actionApi != null)
                    {
                        actionApi.Name = actionEntity.Name;
                        actionApi.Label = actionEntity.Label;
                        actionApi.HttpMethod = actionEntity.HttpMethod;
                        actionApi.Path = actionEntity.Path;
                        actionApi.Sort = actionEntity.Sort;
                        if (actionApi.IsDeleted)
                        {
                            actionApi.IsDeleted = false;
                        }
                        updateApis.Add(actionApi);
                    } else
                    {
                        // 如果actionApi不存在，且controllerApi存在
                        if (controllerApi != null)
                        {
                            actionEntity.Parent = controllerApi;
                            newApis.Add(actionEntity);
                        } else
                        {
                            // 如果actionApi不存在，且controllerApi不存在
                            controlleraApiEntity.Children.Add(actionEntity);
                        }
                    }
                    sort += 1;
                }
                // 如果controllerApi存在
                if (controllerApi != null)
                {
                    controllerApi.Name = controlleraApiEntity.Name;
                    controllerApi.Label = controlleraApiEntity.Label;
                    controllerApi.Path = controlleraApiEntity.Path;
                    controllerApi.Sort = controlleraApiEntity.Sort;
                    if (controllerApi.IsDeleted)
                    {
                        controllerApi.IsDeleted = false;
                    }
                    updateApis.Add(controllerApi);
                } else
                {
                    // 如果controllerApi不存在
                    newApis.Add(controlleraApiEntity);
                }
            }
            // 获取需要更新的路径集合
            var updatePaths = updateApis.Select(u => u.Path).ToHashSet();
            // 在数据库中查找路径不在 updatePaths 中的记录，即需要假删除的数据，排除已经假删除的
            var ids = apis.Where(e => !updatePaths.Contains(e.Path) && !e.IsDeleted).Select(e => e.Id).ToList();

            if (ids.Any())
            {
                result = result ? await _apiRepository.DeleteRangeAsync(ids) : false;
            }
            if (updateApis.Any())
            {
                result = result ? await _apiRepository.UpdateRangeAsync(updateApis) : false;
            }
            if (newApis.Any())
            {
                result = result ? await _apiRepository.AddRangeAsync(newApis) : false;
            }
            result = result ? await _apiRepository.SaveChangesAsync() : false;

            return result;
        }

        #endregion 同步
    }
}